/**
 * 多表同时创建
 * db单例
 * 搜索条件
 */
class Indexed {
  private database = 't'
  private version = 1
  requestInstance: any
  db: any
  private static instant: Indexed
  registeredDB: IModel[] = []

  static get instance(): Indexed {
    if (!Indexed.instant) {
      Indexed.instant = new Indexed()
    }
    return Indexed.instant
  }

  static register(db: IModel): void {
    Indexed.instance.registeredDB.push(db)
  }

  static install(): void {
    Indexed.instance.link().then()
  }

  link() {
    return new Promise<void>((resolve, reject) => {
      if (Indexed.instance.db) {
        resolve()
      }
      this.requestInstance = indexedDB.open(this.database, this.version)
      this.requestInstance.onsuccess = this.onLinkSuccess(resolve)
      this.requestInstance.onupgradeneeded = this.onLinkUpgradeNeeded(resolve)
      this.requestInstance.onerror = this.onError(reject)
    })
  }

  onLinkSuccess(resolve:any) {
    return (event: any) => {
      Indexed.instance.db = event.target.result
      console.log('success')
      resolve()
    }
  }

  onLinkUpgradeNeeded(resolve: any) {
    return (event: any) => {
      const db = Indexed.instance.db = event.target.result
      for (const table of Indexed.instance.registeredDB) {
        if (!db.objectStoreNames.contains(table.table)) {
          table.handle(db)
        } else {
          console.error(`${table.table}表已存在`)
        }
      }
      resolve()
    }
  }

  onError(reject: any) {
    return (event: any) => {
      reject(event.target.error)
    }
  }
}

/**
 * 模型接口
 */
interface IModel {
  table: string
  handle: (db: any) => void
}

/**
 * 条件接口
 */
interface Condition {
  column: string
  condition: string|number
}

abstract class Model implements IModel {
  abstract handle(db: any): void

  abstract table: string;
  abstract condition: Condition;

  link() {
    return Indexed.instance.link()
  }

  create(data: any) {
    this.link().then(_ => {
      this.add(data)
    }).catch(e => {
      console.log(e)
    })
  }

  add(data: any, success?: any, error?: any): void;

  add(data: any[], success?: any, error?: any): void {
    let request
    if (data.length > 0) {
      request = Indexed.instance.db.transaction([this.table], 'readwrite')
      const objStore = request.objectStore(this.table)
      data.forEach(item => {
        objStore.add(item)
      })
    } else {
      request = Indexed.instance.db.transaction([this.table], 'readwrite')
        .objectStore(this.table)
        .add(data)
    }

    request.onsuccess = function(event: any) {
      console.log('数据写入成功')
      if (success) {
        success(event)
      }
    }

    request.onerror = function(event: any) {
      console.log('数据写入失败')
      if (error) {
        error(event)
      }
    }
  }

  find(id: any) {
    return new Promise((resolve, reject) => {
      this.link().then(_ => {
        this.read(id, resolve, reject)
      }).catch(e => {
        reject(e)
      })
    })
  }

  read(id: number, resolve: (data: any) => void, reject: () => void) {
    const request = Indexed.instance.db.transaction([this.table])
      .objectStore(this.table)
      .get(id)

    request.onerror = function(event: any) {
      console.log('事务失败', event)
    }

    request.onsuccess = function(event: any) {
      console.log(event)
      if (request.result) {
        resolve(request.result)
      } else {
        reject()
      }
    }
  }

  delete(id: any) {
    return new Promise((resolve, reject) => {
      this.link().then(_ => {
        this.del(id, resolve, reject)
      }).catch(e => {
        reject(e)
      })
    })
  }

  del(id: any, resolve: any, reject: any) {
    const request = Indexed.instance.db.transaction([this.table], 'readwrite')
      .objectStore(this.table)
      .delete(id)

    request.onerror = function(event: any) {
      console.log('事务失败')
      reject(event)
    }

    request.onsuccess = function(event: any) {
      resolve(event)
    }
  }

  all() {
    return new Promise((resolve, reject) => {
      this.link().then(_ => {
        this.openCursor(resolve, reject)
      }).catch(e => {
        reject(e)
      })
    })
  }

  in(arr: any) {
    return new Promise((resolve, reject) => {
      this.link().then(_ => {
        this.openCursor(resolve, reject, arr)
      }).catch(e => {
        reject(e)
      })
    })
  }

  openCursor(resolve: any, reject: any, inArr?: any) {
    const request = Indexed.instance.db.transaction([this.table])
    let objectStore, keyRange
    if (this.condition) {
      objectStore = request.objectStore(this.table).index(this.condition.column)
      keyRange = IDBKeyRange.only(this.condition.condition)
    } else {
      objectStore = request.objectStore(this.table)
    }

    const v = objectStore.openCursor(keyRange)
    v.onerror = function(event: any) {
      console.log('事务失败')
      reject(event)
    }
    const res: any[] = []
    v.onsuccess = function(event: any) {
      const cursor = event.target.result
      // console.log(cursor)
      if (cursor) {
        if (inArr) {
          if (inArr.indexOf(cursor.value.id) > -1) {
            res.push(cursor.value)
          }
        } else {
          res.push(cursor.value)
        }
        cursor.continue()
      } else {
        resolve(res)
      }
    }
  }

  where(column: string, condition: string): this {
    this.condition = { column, condition }
    return this
  }
}

export {
  Indexed,
  Model
}
